package org.ifi.xechecs.board;

import java.util.ArrayList;
import java.util.List;

public class MoveGenerator {
	public static MoveList generate(Board board){
		MoveList moves = new MoveList();
		byte activePlayer = board.getCurrentPlayer();
		for(byte src = 0; src < 64; src++){
			byte piece = board.getPiece(src);
			if(Piece.getColor(piece) != activePlayer){
				continue;
			}
			switch (piece) {
			//generate King's moves
			case Piece.WK:
                for(byte dst : MoveTables.kingTables[src]){                                                                                                 
                    if( board.getColor(dst) != activePlayer){ 
                        moves.add(new Move(src, dst));
                    }
                    //add casting short
                    if(board.canCastleShort(Piece.WK) && 
                    		board.getPiece((byte)5) == Piece.NONE && 
                    		board.getPiece((byte)6) == Piece.NONE ){
                    	moves.add(new Move(new Position((byte)4), new Position((byte)6)));
                    }
                  //add casting long
                    if(board.canCastleLong(Piece.BK) && 
                    		board.getPiece((byte)3) == Piece.NONE &&
                    		board.getPiece((byte)2) == Piece.NONE &&
                    		board.getPiece((byte)1) == Piece.NONE ){
                    	moves.add(new Move(new Position((byte)4), new Position((byte)2)));
                    }
                }
				break;				
			case Piece.BK:
                for(byte dst : MoveTables.kingTables[src]){                                                                                                 
                    if( board.getColor(dst) != activePlayer){ 
                        moves.add(new Move(src, dst));
                    }
                    //add casting short
                    if(board.canCastleShort(Piece.BK) && 
                    		board.getPiece((byte)62) == Piece.NONE && 
                    		board.getPiece((byte)61) == Piece.NONE ){
                    	moves.add(new Move(new Position((byte)60), new Position((byte)62)));
                    }
                  //add casting long
                    if(board.canCastleLong(Piece.BK) && 
                    		board.getPiece((byte)59) == Piece.NONE &&
                    		board.getPiece((byte)58) == Piece.NONE &&
                    		board.getPiece((byte)57) == Piece.NONE ){
                    	moves.add(new Move(new Position((byte)60), new Position((byte)58)));
                    }
                }
				break;
			//generate Queen's moves
			case Piece.WQ:
			case Piece.BQ:
//				moves.addAll(genRayMove(board, MoveTables.queenTables[src], src, activePlayer));
				
				// Queen = Rook + Bishops
				moves.addAll(genRayMove(board, MoveTables.rookTables[src], src, activePlayer));
				moves.addAll(genRayMove(board, MoveTables.bishopTables[src], src, activePlayer));
				break;
			//generate Rook's moves	
			case Piece.WR:
			case Piece.BR:
				moves.addAll(genRayMove(board, MoveTables.rookTables[src], src, activePlayer));
				break;
				
			//generate Bishop's moves
			case Piece.WB:
			case Piece.BB:
				moves.addAll(genRayMove(board, MoveTables.bishopTables[src], src, activePlayer));
				break;
				
			//generate Knight's moves	
			case Piece.WN:
			case Piece.BN:
                for(byte dst : MoveTables.knightTables[src]){                                                                                                 
                    if( board.getColor(dst) != activePlayer){ 
                        moves.add(new Move(src, dst));
                    }
                }				
				break;			
				//generate PAWN's moves	
				case Piece.WP:
				case Piece.BP:
					moves.addAll(genPawnMove(board, src, activePlayer));
					break;
			default:
				break;
			}
		}
		return moves;
	}
	
	private static MoveList genPawnMove(Board board, byte src, byte activePlayer){
		MoveList moves = new MoveList();
		Position srcPos = new Position(src);
		if(srcPos.getRow() < 1 || srcPos.getRow() > 6){
			return moves;
		}
		byte firstRank = 1;
		byte dir = 1;		
		
		if(activePlayer == Piece.BLACK){
			firstRank = 6;
			dir = -1;
		}
		
		Position dstPos = new Position((byte)(srcPos.getRow() + dir), srcPos.getCol());		
		//move is valid
		if(board.getPiece(dstPos) == Piece.NONE){
			moves.add(new Move(srcPos, dstPos));
		}
		//double move
		if(firstRank == srcPos.getRow()){
			Position doublePos = new Position((byte)(srcPos.getRow() + dir * 2), srcPos.getCol());
			if(board.getPiece(doublePos) == Piece.NONE && board.getPiece(dstPos) == Piece.NONE){
				moves.add(new Move(srcPos, doublePos));
			}
		}
		
		//capture + en passant
		List <Position> nextPos = new ArrayList<Position>();
		if(srcPos.getCol() != 0){
			nextPos.add(new Position((byte)(srcPos.getRow() + dir), (byte) (srcPos.getCol() - 1)));			
		}
		if(srcPos.getCol() != 7){
			nextPos.add(new Position((byte)(srcPos.getRow() + dir), (byte) (srcPos.getCol() + 1)));			
		}
		for(Position dp: nextPos){
			if(board.getPiece(dp) != Piece.NONE && board.getColor(dp.toByte()) != activePlayer){
				moves.add(new Move(srcPos, dp));
			}else if(dp.toByte() == board.getEnPassant() && (srcPos.getRow() != 1 || srcPos.getRow() != 6)){
				moves.add(new Move(srcPos, dp));
			}
		}
		return moves;
	}
	
	private static MoveList genRayMove(Board board, byte[][] rays,  byte src, byte activePlayer){
		MoveList moves = new MoveList();
		for(byte[] ray : rays){
            for(byte dst : ray){
            	byte color = board.getColor(dst);
            	if(color == Piece.NONE){
            		moves.add(new Move(src, dst));
            	}else{
            		if( color != activePlayer){
            			moves.add(new Move(src, dst));
            		}                    
                    break;
                }
            }
		}
		return moves;
	}
}
